// $Id: env.c,v 1.4 2006-09-11 07:42:28 nicolasw Exp $

#include "ieentry.h"
#include "env.h"
#include "yield.h"
#include <stdlib.h>
#include <assert.h>
#include <stdio.h> // para perror()
#include <pthread.h>


struct env_s {
	struct rbtree* _rb;
	unsigned int _size;
};

/* Invariante de representacion muy sencillo:
	. entorno definido
	. tama#o==0 <=> arbol==NULL
*/

#define ENV_INV(e) (e!=NULL &&\
		((e->_size==0 && e->_rb==NULL) || (e->_size>0 && e->_rb!=NULL)))


env
env_create(void)
{
	env e = NULL;

	e = (env)calloc(1, sizeof(struct env_s));
	if (e!=NULL) {
		e->_rb = rbtnew();
		e->_size = 0;
	} else {
		perror("env_create, malloc env:");
	}

	assert(ENV_INV(e));
	return e;
}


void
env_destroy(env e)
{
	assert(ENV_INV(e));
	rbtdestroy(e->_rb);
	free(e);
}


unsigned int
env_size(const env e)
{
	assert(ENV_INV(e));
	return e->_size;
}


/* Asignar VALOR a CLAVE en el entorno.  Si REEMPLAZAR es verdadero
 * se reemplaza el valor de CLAVE por el dado en caso de ya estar definido.
 */ 
void
env_set(env e,
	const char* key, const char* value, const bool replace)
{
	
	yield0();
	
	struct int_env_entry *entry = ieentry_new();
	int assign_ret = 0;
	
	assert(ENV_INV(e));
	assert(entry!=NULL); // no manejamos este error
	yield0();

	assign_ret = ieentry_assign(entry, key, value);
	
	
	if (assign_ret==0 && (replace || !rbtget(e->_rb, entry, NULL))) {
	
		e->_rb = rbtinsert(e->_rb, entry);
		e->_size++;
		yield0();

	}
	
	yield0();
	
	ieentry_destroy(entry); entry = NULL;
	
}


/* Devuelve un puntero a la entrada del entorno interno que contiene el valor
 * de la CLAVE.  Devuelve (null) si el entorno no contiene la CLAVE.
 * ** No hay que modificar este puntero **
 * La estructura NO puede cambiar, luego no hay nuevo entorno para devolver
 */
char*
env_get(const env e, const char* key)
{

	char *retval = NULL;
	struct int_env_entry *entry = ieentry_new();
	assert(ENV_INV(e));
	assert(entry!=NULL);
	
	ieentry_assign(entry, key, NULL);
	rbtget(e->_rb, entry, entry);
	retval = ieentry_rob_val(entry);
	yield0();
	ieentry_destroy(entry); entry = NULL;
	assert(ENV_INV(e));
	

	return retval;
}


/* Elimina la entrada CLAVE del entorno
 * Si no hay entrada con CLAVE, no la elimina.
 *
 * rbtremove no definido, funcion identidad.
 */
void
env_unset(env e, const char* key)
{
	struct int_env_entry *entry = ieentry_new();
	assert(ENV_INV(e));
	assert(entry!=NULL);

	ieentry_assign(entry, key, NULL);
	e->_rb = rbtremove(e->_rb, entry);
	ieentry_destroy(entry); entry = NULL;
}


/* Elimina todas las entradas del entorno
 */
void
env_clear(env e)
{
	assert(ENV_INV(e));
	e->_rb = rbtdestroy(e->_rb);
	e->_size = 0;
	assert(ENV_INV(e));
}
